<template>
  <view class="container">
    <CustomNavbar/>

    <!-- 全局加载遮罩（在数据载入完成前显示） -->
    <view v-if="!isLoaded" class="loading-overlay">
      <view class="loading-box">
        <view class="spinner">
          <view class="dot" v-for="n in 3" :key="n"></view>
        </view>
        <view class="loading-text">数据加载中...</view>
      </view>
    </view>

    <PublicContentLayout>
      <view class="banner-area">
        <view class="btn" @tap="navigateToPartnerRank">
          查看热力伙伴排行榜
        </view>
      </view>

      <scroll-view class="region-scroll" scroll-x="true" :scroll-left="regionScrollLeft" scroll-with-animation="true">
        <view class="region-tab-inner-wrapper">
          <view
            v-for="(tab, idx) in regionTabs"
            :key="idx"
            :id="'region-' + idx"
            class="region-tab"
            :class="{ active: activeRegionIndex === idx }"
            @tap="onRegionClick(idx)"
          >
            {{ tab }}
          </view>
        </view>
      </scroll-view>

      <view class="team-list-box">
        <view class="list-header">
          <text class="header-item rank">排行</text>
          <text class="header-item store">门店</text>
          <text class="header-item score">总分</text>
          <text class="header-item members">人数</text>
          <text class="header-item avg-score">平均分</text>
        </view>
        <scroll-view scroll-y="true" class="list-scroll-area">
          <view class="team-list-item" v-for="(item, index) in teamRankList" :key="index">
            <view class="rank-column">
              <image v-if="item.rank === 1" src="/static/team-top1.png" class="rank-icon"></image>
              <image v-if="item.rank === 2" src="/static/team-top2.png" class="rank-icon"></image>
              <image v-if="item.rank === 3" src="/static/team-top3.png" class="rank-icon"></image>
              <text v-if="item.rank > 3" class="rank-text">{{ item.rank }}</text>
            </view>
            <view class="store-name" style="display:flex;align-items:center;">
              <text style="flex:1;">{{ item.storeName }}</text>
            </view>
            <text class="score-value">{{ item.totalScore }}</text>
            <text class="member-count">{{ item.members }}</text>
            <text class="avg-score-value">{{ item.avgScore }}</text>
          </view>
        </scroll-view>
        <!-- 并列弹窗（与个人页样式一致） -->
        <view v-if="showTieModal" class="modal-overlay">
          <view class="modal-box">
            <view class="modal-header">
              <text>{{ tieTitle }}</text>
              <view class="close-btn" @tap="closeTieModal">关闭</view>
            </view>
            <scroll-view class="modal-list" scroll-y="true">
              <view class="team-list-item" v-for="(item, idx) in tieModalList" :key="idx">
                <text class="partner-name" style="flex:1;">{{ item.name }}</text>
                <text class="score-value">{{ item.totalScore }}</text>
              </view>
            </scroll-view>
          </view>
        </view>
        <view class="my-store-info" v-if="myStoreInfo.rank !== '-'">
          <view class="team-list-item my-team-list-item">
            <view class="rank-column">
              <text class="rank-text" style="color: #3D2A2A;">{{ myStoreInfo.rank }}</text>
            </view>
            <text class="store-name" style="color: #3D2A2A;">{{ myStoreInfo.storeName }}</text>
            <text class="score-value" style="color: #3D2A2A;">{{ myStoreInfo.totalScore }}</text>
            <text class="member-count" style="color: #3D2A2A;">{{ myStoreInfo.members }}</text>
            <text class="avg-score-value" style="color: #3D2A2A;">{{ myStoreInfo.avgScore }}</text>
          </view>
          <view class="my-contribution">
            <view class="my-contribution-text">我的贡献: {{ myStoreInfo.totalScore }}</view>
          </view>
        </view>
      </view>
    </PublicContentLayout>
  </view>
</template>

<script>
import CustomNavbar from '@/components/CustomNavbar.vue'
import PublicContentLayout from '@/components/PublicContentLayout.vue'
import createLiteTable from '@/utils/tableLite.js'
import { searchTeamAnswerInfo } from '@/api/base.js'
import AllUserInfo from '../../static/AllUserInfo'

export default {
  components: { CustomNavbar, PublicContentLayout },
  data() {
    return {
      // 动态区域标签（从表格生成），初始只有全国
      regionTabs: ['全国'],
      activeRegionIndex: 0,
      regionScrollLeft: 0,
      // 从表格计算得到的门店排行
      teamRankList: [],
      topPartnerList: [],
      // 原始表格数据缓存
      tableInfo: [],
      // 当前用户工号
      currentUserWorkNumber: '',
      // 当前用户所在门店信息
      myStoreInfo: {
        rank: '-',
        storeName: '--',
        totalScore: 0,
        members: 0,
        avgScore: 0,
        myContribution: 0,
        visible: false
      },
      // 并列弹窗相关
      showTieModal: false,
      tieTitle: '',
      tieModalList: [],
      isShowLookBox: false, // 是否是参赛人员
      isLoaded: false, // 数据是否加载完成
      renderStabilityTimer: null,
    }
  },
  async onLoad() {
    await this.getCurrentUserWorkNumber();
    await this.getTableInfo();
    dd.getStorage({
      key: 'isShowLookBox',
      success: (res) => {
        this.isShowLookBox = res.data;
      },
    });
  },
  onReady() {
    // 修复Android渲染问题
    setTimeout(() => { dd.setNavigationBar({ title: '' }); }, 100);
    const menu = dd.getMenuButtonBoundingClientRect && dd.getMenuButtonBoundingClientRect();
    if (menu) {
      console.log(menu, "胶囊按钮位置");
    } else {
      console.log("dd.getMenuButtonBoundingClientRect 不可用，跳过胶囊按钮位置检测");
    }
    // 初始化时将默认激活的tab居中
    this.$nextTick(() => {
      this.centerActiveRegion();
    });
  },
  methods: {
    /**
     * Persist split TableInfo into storage when large.
     * If list.length > chunkSize (500) it will write TableInfo1..N and set TableInfo_count.
     * Returns a Promise that resolves once writes complete.
     */
    saveTableInfo(list) {
      return new Promise((resolve) => {
        try {
          const chunkSize = 300;
          const chunks = [];
          const arr = Array.isArray(list) ? list : (Array.isArray(list.data) ? list.data : []);
          for (let i = 0; i < arr.length; i += chunkSize) {
            chunks.push(arr.slice(i, i + chunkSize));
          }
          // Ensure at least one chunk (empty arrays still stored as one chunk)
          if (chunks.length === 0) chunks.push([]);
          const newCount = chunks.length;

          // Try to read previous count to clean up extra keys
          try {
            dd.getStorage({
              key: 'TableInfo_count',
              success: (res) => {
                const oldCount = Number(res && res.data) || 0;
                if (oldCount > newCount) {
                  for (let k = newCount + 1; k <= oldCount; k++) {
                    try { dd.removeStorage({ key: 'TableInfo' + k }); } catch (e) {}
                  }
                }
              },
              fail: () => {},
            });
          } catch (e) {}

          const setPromises = chunks.map((chunk, idx) => new Promise((resSet) => {
            try {
              dd.setStorage({
                key: 'TableInfo' + (idx + 1),
                data: createLiteTable(chunk),
                success: () => resSet(),
                fail: () => resSet(),
              });
            } catch (e) { resSet(); }
          }));

          Promise.all(setPromises).then(() => {
            try {
              dd.setStorage({ key: 'TableInfo_count', data: newCount, success: () => resolve(), fail: () => resolve() });
            } catch (e) { resolve(); }
          }).catch(() => {
            try { dd.setStorage({ key: 'TableInfo_count', data: newCount, success: () => resolve(), fail: () => resolve() }); } catch (e) { resolve(); }
          });
        } catch (e) { resolve(); }
      });
    },

    /**
     * Load TableInfo by reading TableInfo_count and then TableInfo1..N and concatenating.
     * Falls back to legacy single 'TableInfo' key if TableInfo_count not present.
     * Resolves to an array (possibly empty).
     */
    loadTableInfo() {
      return new Promise((resolve) => {
        try {
          dd.getStorage({
            key: 'TableInfo_count',
            success: (res) => {
              const count = Number(res && res.data) || 0;
              if (count <= 0) { resolve([]); return; }
              const gets = [];
              for (let i = 1; i <= count; i++) {
                gets.push(new Promise((resGet) => {
                  try {
                    dd.getStorage({ key: 'TableInfo' + i, success: (r) => resGet(Array.isArray(r.data) ? r.data : (r.data || [])), fail: () => resGet([]) });
                  } catch (e) { resGet([]); }
                }));
              }
              Promise.all(gets).then((parts) => {
                try { resolve([].concat(...parts)); } catch (e) { resolve([]); }
              }).catch(() => resolve([]));
            },
            fail: () => {
              // Fallback to legacy single key
              try {
                dd.getStorage({
                  key: 'TableInfo',
                  success: (r) => { resolve(Array.isArray(r.data) ? r.data.slice() : (r.data || [])); },
                  fail: () => { resolve([]); }
                });
              } catch (e) { resolve([]); }
            },
          });
        } catch (e) { resolve([]); }
      });
    },

    // Safe loader that falls back to legacy single-key storage when loadTableInfo isn't available
    safeLoadTableInfo() {
      if (typeof this.loadTableInfo === 'function') return this.loadTableInfo();
      return new Promise((resolve) => {
        try {
          dd.getStorage({ key: 'TableInfo', success: (r) => { resolve(Array.isArray(r.data) ? r.data.slice() : (r.data || [])); }, fail: () => { resolve([]); } });
        } catch (e) { resolve([]); }
      });
    },
    /**
     * 获取表格信息
     */
    async getTableInfo() {
      // Fetch grouped team ranking data from server using the official API (scene=TEAM)
      try {
        // Read SelectedTrack and isShowLookBox concurrently
        const [track, isShow] = await Promise.all([
          new Promise((resolve) => {
            try {
              dd.getStorage({ key: 'SelectedTrack', success: (res) => { resolve(res && res.data ? res.data : null); }, fail: () => { resolve(null); } });
            } catch (e) { resolve(null); }
          }),
          new Promise((resolve) => {
            try {
              dd.getStorage({ key: 'isShowLookBox', success: (res) => { resolve(!!res && !!res.data); }, fail: () => { resolve(false); } });
            } catch (e) { resolve(false); }
          })
        ]);

        // If user is a participant (isShow === true) -> pass workNumber and empty track
        // If non-participant -> pass track and empty workNumber
        const workNumberParam = isShow ? this.currentUserWorkNumber || '' : null;
        const trackParam = isShow ? '' : (track || '');

        const resp = await searchTeamAnswerInfo(workNumberParam, trackParam, 'TEAM');
        const groups = Array.isArray(resp) ? resp.slice() : (resp && Array.isArray(resp.msg) ? resp.msg.slice() : []);
        this.tableInfo = groups;
      } catch (e) {
        this.tableInfo = [];
      }
      this.buildRankLists();
      try { this.waitForRenderThenShow(); } catch (e) { this.isLoaded = true; }
    },
    /**
     * 获取当前用户工号
     */
     getCurrentUserWorkNumber() {
      return new Promise((resolve) => {
      dd.getStorage({
        key: 'CurrentUserWorkNumber',
        success: (res) => {
          console.log(res, "当前用户工号");
          this.currentUserWorkNumber = res.data;
          resolve();
        },
        fail: () => {
          this.currentUserWorkNumber = '';
          resolve();
        }
      })
     })
    },
    onRegionClick(idx) {
      this.activeRegionIndex = idx;
      this.centerActiveRegion();
      this.buildRankLists();
    },
    centerActiveRegion() {
      // 尝试把活跃标签滚动到区域的中间
      const query = uni.createSelectorQuery();
      query.select('#region-' + this.activeRegionIndex).boundingClientRect();
      query.select('.region-scroll').boundingClientRect();
      // 获取当前 scroll-view 的 scrollLeft，用于计算相对位置
      query.select('.region-scroll').scrollOffset();
      query.exec((res) => {
        const tabRect = res[0];
        const scrollRect = res[1];
        const scrollOffset = res[2];
        const currentScrollLeft = (scrollOffset && scrollOffset.scrollLeft) ? scrollOffset.scrollLeft : 0;
        if (tabRect && scrollRect) {
          // 计算标签在滚动内容中的中心位置：
          // 标签相对于可视区域的 left 减去可视区域的 left 再加上当前的 scrollLeft
          const tabPositionInScroll = (tabRect.left - scrollRect.left) + currentScrollLeft;
          // 目标 scrollLeft：使当前标签居中
          const targetScrollLeft = Math.max(0, tabPositionInScroll - (scrollRect.width - tabRect.width) / 2);
          this.regionScrollLeft = targetScrollLeft;
        }
      });
    },
    navigateToPartnerRank() {
      uni.redirectTo({
        url: '/pages/partnerRank/index'
      });
    },
    handleBack() {
      dd.navigateBack();
    },
    // helper to parse numbers safely (remove commas, percent signs)
    toNumber(v) {
      if (v === null || v === undefined) return 0;
      if (typeof v === 'number') return Number.isFinite(v) ? v : 0;
      let s = String(v).trim();
      s = s.replace(/[,\s%]/g, '');
      const n = Number(s);
      return Number.isFinite(n) ? n : 0;
    },
    // build teamRankList and myStoreInfo based on server-provided tableInfo groups
    buildRankLists() {
      const groups = Array.isArray(this.tableInfo) ? this.tableInfo.slice() : [];

      // regionTabs from groupName (deduplicated, server order)
      const tabs = [];
      const seen = new Set();
      groups.forEach(g => { const n = g && g.groupName ? String(g.groupName).trim() : ''; if (n && !seen.has(n)) { seen.add(n); tabs.push(n); } });
      this.regionTabs = tabs;
      if (this.activeRegionIndex >= this.regionTabs.length) this.activeRegionIndex = 0;
      const selectedRegion = this.regionTabs[this.activeRegionIndex];

      // collect items for selected region
      const rawItems = [];
      groups.forEach(g => { if (g && g.groupName === selectedRegion && Array.isArray(g.teamAnswerItemInfoVOList)) rawItems.push(...g.teamAnswerItemInfoVOList.map(it => Object.assign({}, it, { _groupName: g.groupName }))); });

      // lookup helper
      const findUserInfo = (id) => { if (!id || typeof AllUserInfo === 'undefined' || !Array.isArray(AllUserInfo)) return null; const s = String(id); try { return AllUserInfo.find(u => String(u.staffId) === s || String(u.workNumber) === s || String(u.staff_id) === s || String(u.id) === s) || null; } catch (e) { return null; } };

      const items = rawItems.map(it => {
        const id = it.workNumber || it.staffId || it.id || it.userId || it.uid;
        const info = findUserInfo(id);
        const avatar = (it.avatarImage || it.avatar || (info && (info.avatarImage || info.avatar)) || '/static/default-avatar.jpg');
        const name = it.name || (info && (info.name || info.flowerName)) || '--';
        return Object.assign({}, it, { avatarImage: avatar, storeName: name, workNumber: it.workNumber });
      });

      // top three from server order by distinct rank
      const topGroups = [];
      const seenRanks = new Set();
      for (let i = 0; i < items.length && topGroups.length < 3; i++) {
        const it = items[i];
        if (seenRanks.has(it.rank)) continue; seenRanks.add(it.rank);
        const members = items.filter(x => x.rank === it.rank);
        if (members.length > 1) topGroups.push({ rank: it.rank, name: '多名并列', totalScore: it.totalScore, isTieGroup: true, membersCount: members.length });
        else topGroups.push({ rank: it.rank, name: it.name, totalScore: it.totalScore, isTieGroup: false, workNumber: it.workNumber, avatarImage: it.avatarImage || '/static/default-avatar.jpg', title: it.title || '', storeName: it.name });
      }
      this.topPartnerList = topGroups; // reuse topPartnerList to drive top UI
      // teamRankList: include all ranks starting from 1 (do not exclude top groups)
      this.teamRankList = items.map(it => ({ rank: it.rank, storeName: it.name, totalScore: it.totalScore, members: it.peopleCount, avgScore: it.avgScore, avatar: it.avatarImage || '/static/default-avatar.jpg' }));

      // myStoreInfo: prefer group-level personal fields, else find in items
      let myInfo = { rank: '', storeName: '', totalScore: 0, members: 0, avgScore: 0, myContribution: 0 };
      const hasPersonalSharedFields = (g) => {
        if (!g) return false;
        // Consider a group to have personal-shared-fields if it contains the personal* properties
        // (even if their values are null). This allows the UI to show the group's "my store" box
        // when switching regions that include those keys.
        try {
          return ('personalRank' in g) || ('personalName' in g) || ('personalTotalScore' in g) || ('personalPeopleCount' in g) || ('personalAvgScore' in g);
        } catch (e) {
          return false;
        }
      };

      let grp = groups.find(g => g && g.groupName === selectedRegion && hasPersonalSharedFields(g));
      if (!grp) grp = groups.find(g => hasPersonalSharedFields(g));
      if (grp) {
        myInfo = {
          rank: (grp.personalRank !== undefined && grp.personalRank !== null) ? grp.personalRank : '-',
          storeName: (grp.personalName !== undefined && grp.personalName !== null) ? grp.personalName : '--',
          totalScore: (grp.personalTotalScore !== undefined && grp.personalTotalScore !== null) ? grp.personalTotalScore : 0,
          members: (grp.personalPeopleCount !== undefined && grp.personalPeopleCount !== null) ? grp.personalPeopleCount : 0,
          avgScore: (grp.personalAvgScore !== undefined && grp.personalAvgScore !== null) ? grp.personalAvgScore : 0,
          myContribution: 0,
          visible: true
        };
      } else {
        const p = items.find(it => it.workNumber && String(it.workNumber) === String(this.currentUserWorkNumber));
        if (p) myInfo = { rank: p.rank, storeName: p.name, totalScore: p.totalScore, members: p.peopleCount, avgScore: p.avgScore, myContribution: 0 };
      }
      // ensure visible flag for fallback user-item match
      if (!myInfo.visible) myInfo.visible = (myInfo.rank !== '-' && myInfo.rank !== undefined) || (myInfo.storeName && myInfo.storeName !== '--');
      this.myStoreInfo = myInfo;
    },
    openTieModal(score) {
      if (score === null || score === undefined) return;
      this.tieTitle = `总分: ${score} 的并列名单`;
      const table = Array.isArray(this.tableInfo) ? this.tableInfo.slice() : [];
      const toNumberLocal = (v) => { const n = Number(v); return Number.isFinite(n) ? n : 0; };

      // build map of stores and their aggregated scores using same rules as buildRankLists
      const computeForItem = (it) => {
        const direct = toNumberLocal(it.totalScore);
        if (direct) return direct;
        const practiceTotal = toNumberLocal(it.practiceTotalScore) || toNumberLocal(it.practiceScore);
        const examTotal = toNumberLocal(it.examTotalScore) || toNumberLocal(it.examScore);
        if (practiceTotal || examTotal) return practiceTotal + examTotal;
        const practiceCorrect = toNumberLocal(it.practiceCorrectQuestions) || toNumberLocal(it.practiceCorrect);
        const examCorrect = toNumberLocal(it.examCorrectQuestions) || toNumberLocal(it.examCorrect);
        if (practiceCorrect || examCorrect) return practiceCorrect + examCorrect;
        const alt1 = toNumberLocal(it.practiceTotalQuestions) || 0;
        const alt2 = toNumberLocal(it.examTotalQuestions) || 0;
        return alt1 + alt2;
      };

      const storeMapLocal = {};
      table.forEach(it => {
        const key = (it.storeName || it.store || it.team || it.shopName || '未知门店').toString();
        const sc = computeForItem(it);
        if (!storeMapLocal[key]) storeMapLocal[key] = { storeName: key, totalScore: 0, avatar: '' };
        storeMapLocal[key].totalScore += sc;
        if (!storeMapLocal[key].avatar) storeMapLocal[key].avatar = it.storeAvatar || it.avatarImage || it.avatar || '/static/default-avatar.jpg';
      });

      const stores = Object.keys(storeMapLocal).map(k => storeMapLocal[k]).filter(s => s.totalScore === score);
      this.tieModalList = stores.map((s, idx) => ({ rank: idx + 1, name: s.storeName, totalScore: s.totalScore, avatar: s.avatar }));
      this.showTieModal = true;
    },
    closeTieModal() {
      this.showTieModal = false;
      this.tieModalList = [];
      this.tieTitle = '';
    },
    waitForRenderThenShow() {
      this.renderStabilityTimer = setTimeout(() => {
        this.isLoaded = true;
        this.renderStabilityTimer = null;
      }, 100); // 100ms for stability
    },
    clearRenderStabilityTimer() {
      if (this.renderStabilityTimer) {
        clearTimeout(this.renderStabilityTimer);
        this.renderStabilityTimer = null;
      }
    }
  }
}
</script>

<style scoped>
.container {
  min-height: 100vh;
  box-sizing: border-box;
  background: url('/static/team-rank-bg.png') no-repeat;
  background-size: 100%;
  background-color: #000;
  padding-bottom: 170rpx;
}
.banner-area {
  position: relative;
  height: 540rpx;
  width: 100%;
}
.btn {
  position: fixed;
  top: 438rpx;
  right: -2rpx;
  width: 302rpx;
  height: 64rpx;
  background: linear-gradient(191deg, 
    rgba(203, 192, 177, 1), 
    rgba(74, 64, 56, 1));
  display: flex;
  align-items: center;
  font-size: 28rpx;
  color: #FDEBC2;
  padding-left: 26rpx;
  border-radius: 32rpx 0rpx 0rpx 32rpx;
  z-index: 1000;
  box-sizing: border-box;
}
.btn::before {
  content: '';
  position: absolute;
  top: 2rpx;
  left: 2rpx;
  right: 2rpx;
  bottom: 2rpx;
  background: linear-gradient(180deg, #281A0F 0%, #1B0F03 100%);
  border-radius: 30rpx 0rpx 0rpx 30rpx;
  z-index: -1;
}
.region-scroll {
  height: 64rpx;
  overflow: hidden;
}
.region-tab-inner-wrapper {
  display: flex;
  flex-wrap: nowrap;
  padding: 0 20rpx;
}
.region-tab {
  display: inline-block;
  padding: 0 24rpx;
  height: 56rpx;
  line-height: 56rpx;
  margin-right: 24rpx;
  white-space: nowrap;
  color: #979797;
  font-size: 28rpx;
  background: linear-gradient( 360deg, #291A0F 0%, #231807 97%);
  border-radius: 40rpx 40rpx 40rpx 40rpx;
}
.region-tab.active {
  background: linear-gradient( 90deg, #E3CBB4 0%, #A5836C 100%);
  color: #3D2A2A;
}

.team-list-box {
  width: 702rpx;
  padding: 20rpx;
  margin: 0 auto;
}

.list-header {
  height: 40rpx;
  display: flex;
  justify-content: space-between;
  font-size: 24rpx;
  color: #979797;
  text-align: center;
  margin-bottom: 10rpx;
  padding: 0 24rpx;
}

.header-item {
  flex: 1;
  text-align: center;
}

.header-item.rank {
  flex: 1;
}

.header-item.store {
  flex: 4;
  text-align: center;
}

.list-scroll-area {
  height: 928rpx;
  background: linear-gradient( 360deg, #241F17 0%, #352E23 97%);
  border-radius: 24rpx 24rpx 24rpx 24rpx;
  padding: 24rpx;
  box-sizing: border-box;
}

.team-list-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 24rpx;
  font-weight: 600;
  color: #FBEDD4;
  margin-bottom: 46rpx;
}

.rank-column {
  flex: 1;
  font-size: 32rpx;
  color: #D8D8D8;
  display: flex;
  justify-content: center;
  align-items: center;
}

.rank-icon {
  width: 72rpx;
  height: 72rpx;
}

.rank-text {
  font-weight: bold;
}

.store-name {
  flex: 4;
  text-align: left;
  font-size: 24rpx;
  font-weight: 600;
  color: #FBEDD4;
  padding-left: 14rpx;
  box-sizing: border-box;
}

.store-name-text {
  font-size: 24rpx;
  color: #FBEDD4;
  font-weight: 600;
}

.score-value,
.member-count,
.avg-score-value {
  flex: 1;
  text-align: center;
  font-size: 24rpx;
  font-weight: 600;
  color: #FBEDD4;
}

.my-store-info {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 168rpx;
  background: linear-gradient( 90deg, #E3CBB4 0%, #A5836C 100%);
  box-sizing: border-box;
  padding: 18rpx 48rpx 0;
}

.my-team-list-item {
  margin-bottom: 0rpx;
}

.my-contribution-text {
  display: inline-block;
  height: 46rpx;
  line-height: 46rpx;
  background: linear-gradient( 270deg, #58403F 0%, #B69173 100%);
  border-radius: 23rpx;
  font-size: 24rpx;
  font-weight: 400;
  color: #FBEDD4;
  padding: 0 16rpx;
  margin-left: 90rpx;
}

.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.modal-overlay .modal-box {
  width: 600rpx;
  background: #3E382F;
  border-radius: 24rpx;
  padding: 40rpx;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
}

.modal-overlay .modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  margin-bottom: 30rpx;
}

.modal-overlay .modal-header text {
  font-size: 36rpx;
  font-weight: 600;
  color: #FBEDD4;
  text-align: center;
}

.modal-overlay .close-btn {
  font-size: 28rpx;
  color: #D8D8D8;
  padding: 10rpx 20rpx;
  border-radius: 30rpx;
  border: 2rpx solid #D8D8D8;
}

.modal-overlay .modal-list {
  width: 100%;
  max-height: 600rpx;
  overflow-y: auto;
}

.modal-overlay .team-list-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 80rpx;
  padding: 0 20rpx;
  border-bottom: 1rpx solid #544840;
}

.modal-overlay .team-list-item:last-child { border-bottom: none; }

.modal-overlay .avatar-small {
  width: 60rpx;
  height: 60rpx;
  border-radius: 30rpx;
  margin-right: 20rpx;
  overflow: hidden;
}

.modal-overlay .partner-name {
  flex: 1;
  font-size: 28rpx;
  color: #FBEDD4;
  margin-right: 20rpx;
}

.avatar-btn {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 80rpx;
  height: 48rpx;
  border-radius: 16rpx;
  background-color: #999;
  color: #fff;
  font-size: 24rpx;
}

/* 全局加载遮罩样式 */
.loading-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.7);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 999;
}

.loading-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  background: #3E382F;
  border-radius: 24rpx;
  padding: 40rpx;
  box-sizing: border-box;
}

.spinner {
  width: 80rpx;
  height: 80rpx;
  position: relative;
  margin-bottom: 20rpx;
}

.spinner .dot {
  position: absolute;
  width: 16rpx;
  height: 16rpx;
  background: #D8D8D8;
  border-radius: 50%;
  animation: dot-pulse 1.2s infinite ease-in-out;
}

.spinner .dot:nth-child(1) { top: 0; left: 30rpx; animation-delay: -0.32s; }
.spinner .dot:nth-child(2) { top: 10rpx; left: 10rpx; animation-delay: -0.16s; }
.spinner .dot:nth-child(3) { top: 30rpx; left: 0rpx; animation-delay: 0s; }

@keyframes dot-pulse {
  0%, 80%, 100% { transform: scale(0.8); opacity: 0.6; }
  40% { transform: scale(1); opacity: 1; }
}

.loading-text {
  font-size: 28rpx;
  color: #D8D8D8;
  font-weight: 400;
}
</style> 